﻿#include "utagwidget.h"
#include "uapplication.h"
#include "style/upalette.h"
#include "style/uproxystyle.h"
#include "style/ustylepainter.h"
#include "ulineedit.h"

#include <QPainterPath>
#include <QPushButton>
#include <QStyleOptionButton>
#include <QStylePainter>
#include <QTextDocument>
#include <QVBoxLayout>

class UTagCloseButton : public QPushButton
{
public:
    UTagCloseButton(QWidget* parent = Q_NULLPTR);

    void setBackgroundRole(int role);

    void setButtonFeature(UTagWidgetStyleOption::TagFeature feature);

protected:
    void paintEvent(QPaintEvent* event) Q_DECL_OVERRIDE;

private:
    int m_role = QPalette::Button;
    UTagWidgetStyleOption::TagFeature m_feature = UTagWidgetStyleOption::DeepTag;
};

UTagCloseButton::UTagCloseButton(QWidget* parent) : QPushButton(parent)
{
    setFlat(true);
}

void UTagCloseButton::setBackgroundRole(int role)
{
    if (m_role != role)
    {
        m_role = role;
        update();
    }
}

void UTagCloseButton::setButtonFeature(UTagWidgetStyleOption::TagFeature feature)
{
    if (m_feature != feature)
    {
        m_feature = feature;
        update();
    }
}

void UTagCloseButton::paintEvent(QPaintEvent* event)
{
    Q_UNUSED(event)

    QStyleOptionButton btn;
    initStyleOption(&btn);

    QStylePainter stylePainter(this);
    stylePainter.setRenderHint(QPainter::Antialiasing);

    QPalette::ColorGroup cg = UProxyStyle::colorGroup(&btn, this);
    UPalette* palette = uApp->applicationPalette();
    QColor brush = palette->color(cg, m_role);
    QColor penColor = brush;
    QColor backGroudColor = Qt::transparent;
    if (m_feature == UTagWidgetStyleOption::DeepTag)
    {
        penColor = palette->color(cg, QPalette::HighlightedText);
        brush = brush.darker(120);
    }

    if (btn.state & QStyle::State_Enabled)
    {
        if (btn.state & QStyle::State_MouseOver)
        {
            penColor = palette->color(cg, QPalette::HighlightedText);
            backGroudColor = brush;
        }
    }

    if (isDown())
    {
        penColor = palette->color(cg, QPalette::HighlightedText);
        backGroudColor = brush.darker(130);
    }

    QPainterPath painterPath;
    painterPath.addRoundedRect(rect(), this->height() / 2, this->height() / 2);
    stylePainter.fillPath(painterPath, backGroudColor);

    stylePainter.setPen(penColor);
    QPointF center = rect().center() + QPointF(1, 1);
    double wid = this->height() / 6.0;
    stylePainter.setClipRect(QRect(center.x() - wid, center.y() - wid, wid * 2, wid * 2));
    stylePainter.drawLine(QPointF(center.x() - wid, center.y() - wid), QPointF(center.x() + wid, center.y() + wid));
    stylePainter.drawLine(QPointF(center.x() - wid, center.y() + wid), QPointF(center.x() + wid, center.y() - wid));
}

class UTagWidgetPrivate
{
public:
    UTagWidgetPrivate(UTagWidget* q) : q_ptr(q) {}
    ~UTagWidgetPrivate() {}

    void init();

    void initConnection();

private:
    Q_DECLARE_PUBLIC(UTagWidget)
    Q_DISABLE_COPY(UTagWidgetPrivate)
    UTagWidget* const q_ptr = nullptr;

    QTextDocument doc;
    QString text;
    Qt::TextFormat format = Qt::TextFormat::PlainText;
    bool enabled = false;
    int margins = 6;
    int backgroundRole = QPalette::WindowText;
    UTagWidgetStyleOption::TagFeature feature = UTagWidgetStyleOption::DeepTag;
    UTagCloseButton* closeButton = nullptr;
    QHBoxLayout* layout = nullptr;
    ULineEdit* lineEdit = nullptr;
};

void UTagWidgetPrivate::init()
{
    Q_Q(UTagWidget);

    lineEdit = new ULineEdit(q);
    lineEdit->setRadius(4);
    lineEdit->setBorderWidth(0);
    lineEdit->setHoverEnabled(false);
    lineEdit->setVisible(false);
    closeButton = new UTagCloseButton(q);
    closeButton->setVisible(false);
    layout = new QHBoxLayout;
    layout->setContentsMargins(margins, margins, margins, margins);
    layout->addWidget(lineEdit);
    layout->addWidget(closeButton);
    layout->setAlignment(closeButton, Qt::AlignRight | Qt::AlignVCenter);
    q->setLayout(layout);

    initConnection();
}

void UTagWidgetPrivate::initConnection()
{
    Q_Q(UTagWidget);

    QObject::connect(lineEdit, &ULineEdit::returnPressed, q, &UTagWidget::onEditFinished);

    QObject::connect(lineEdit, &ULineEdit::editingFinished, q, &UTagWidget::onEditFinished);
}

UTagWidget::UTagWidget(QWidget* parent) : QWidget(parent)
    , d_ptr(new UTagWidgetPrivate(this))
{
    Q_D(UTagWidget);
    d->init();
}

UTagWidget::~UTagWidget()
{
}

void UTagWidget::setButtonFeature(UTagWidgetStyleOption::TagFeature feature)
{
    Q_D(UTagWidget);

    if (d->feature != feature)
    {
        d->feature = feature;
        d->closeButton->setButtonFeature(feature);
        update();
    }
}

void UTagWidget::setBackgroundRole(int role)
{
    Q_D(UTagWidget);

    if (d->backgroundRole != role)
    {
        d->backgroundRole = role;
        d->closeButton->setBackgroundRole(role);
        update();
    }
}

void UTagWidget::setTextFormat(const Qt::TextFormat &format)
{
    Q_D(UTagWidget);
    d->format = format;
}

void UTagWidget::setText(const QString &text)
{
    Q_D(UTagWidget);

    if (d->text != text)
    {
        d->text = text;
        if (Qt::TextFormat::RichText == d->format)
        {
            d->doc.setHtml(text);
        }
        update();
    }
}

QString UTagWidget::text() const
{
    Q_D(const UTagWidget);

    return d->text;
}

QString UTagWidget::plainText() const
{
    Q_D(const UTagWidget);

    if (Qt::TextFormat::RichText == d->format)
    {
        return d->doc.toPlainText();
    }
    else
    {
        return d->text;
    }
}

void UTagWidget::setMargin(int margins)
{
    Q_D(UTagWidget);

    if (d->margins != margins)
    {
        d->margins = margins;
        d->layout->setContentsMargins(margins, margins, margins, margins);
        update();
    }
}

void UTagWidget::clear()
{
    Q_D(UTagWidget);

    d->doc.clear();
    d->text.clear();
    update();
}

void UTagWidget::setEditEnable(bool enabled)
{
    Q_D(UTagWidget);

    if (d->enabled != enabled)
    {
        d->enabled = enabled;
        d->closeButton->setVisible(d->enabled);
    }
}

bool UTagWidget::editEnable() const
{
    Q_D(const UTagWidget);

    return d->enabled;
}

void UTagWidget::onEditFinished()
{
    Q_D(const UTagWidget);

    d->lineEdit->setVisible(false);
    d->closeButton->setVisible(true);
    d->layout->setContentsMargins(d->margins, d->margins, d->margins, d->margins);
    auto text = d->lineEdit->text().trimmed();
    if (!text.isEmpty())
    {
        setText(d->lineEdit->text().trimmed());
    }
}

void UTagWidget::paintEvent(QPaintEvent* event)
{
    Q_D(UTagWidget);

    if ((Qt::TextFormat::RichText == d->format) || Qt::mightBeRichText(text()))
    {
        QPainter painter(this);
        painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
        QRect textRect = rect();
        if (d->enabled)
        {
            textRect.setWidth(rect().width() - rect().height());
        }
        textRect = rect().adjusted(d->margins, d->margins, 0, -d->margins);
        d->doc.setTextWidth(textRect.width());
        d->doc.drawContents(&painter, textRect);
    }
    else
    {
        UTagWidgetStyleOption tag;
        initStyleOption(&tag);
        UStylePainter stylePainter(this);
        stylePainter.drawControl(UProxyStyle::CE_UTagWidget, tag);
    }
}

void UTagWidget::initStyleOption(UTagWidgetStyleOption* option) const
{
    Q_D(const UTagWidget);
    option->initFrom(this);

    option->text = d->text;
    option->feature = d->feature;
    option->isEdit = d->enabled;
    option->margins = d->margins;

    UPalette* palette = uApp->applicationPalette();
    QPalette::ColorGroup cg = UProxyStyle::colorGroup(option, this);
    QColor textColor = palette->color(cg, d->backgroundRole);
    QColor backgroundColor = Qt::transparent;
    QColor penColor = Qt::transparent;
    if (option->feature == UTagWidgetStyleOption::DeepTag)
    {
        textColor = palette->color(cg, QPalette::HighlightedText);
        backgroundColor = palette->color(cg, d->backgroundRole);
    }

    if (option->feature == UTagWidgetStyleOption::LightTag)
    {
        backgroundColor = textColor;
        backgroundColor.setAlphaF(0.2);
        penColor = backgroundColor.darker(120);
    }

    if (option->feature == UTagWidgetStyleOption::PlainTag)
    {
        penColor = textColor;
    }

    option->backgroundColor = backgroundColor;
    option->foregroundColor = textColor;
    option->borderColor = penColor;
}

void UTagWidget::resizeEvent(QResizeEvent* event)
{
    Q_D(UTagWidget);

    int width = height() - (2 * d->margins);
    d->closeButton->setFixedSize(width, width);
    QWidget::resizeEvent(event);
}

QSize UTagWidget::sizeHint() const
{
    Q_D(const UTagWidget);
    QRect size = fontMetrics().boundingRect(d->text);
    int width = size.width() + 2 * d->margins;
    int height = size.height() + 2 * d->margins;
    return d->enabled ? QSize(width + height, height) :
           QSize(width, height);
}

QSize UTagWidget::minimumSizeHint() const
{
    return sizeHint();
}

void UTagWidget::mouseDoubleClickEvent(QMouseEvent* event)
{
    Q_D(UTagWidget);

    if (d->enabled)
    {
        d->lineEdit->setText(text());
        d->lineEdit->setVisible(true);
        d->lineEdit->setFocus();
        d->closeButton->setVisible(false);
        d->layout->setContentsMargins(1, 1, 1, 1);
        d->lineEdit->setFixedHeight(height() - 2);
    }

    QWidget::mouseDoubleClickEvent(event);
}
